﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.UI;
using Emgu.CV.Structure;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace ImgTransmitPlus
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, WIDTH);
            capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, HEIGHT);

            int w = panel_imgs.Width;
            int h = panel_imgs.Height;
            // 在面板panel_imgs上添加显示控件，用于显示每个切片
            for (int i = 0; i < NUM_SLICE; i++)
            {
                ImageBox imgb = new ImageBox();
                imgb.Left = 0;
                imgb.Top = i * h / NUM_SLICE;
                imgb.Width = w;
                imgb.Height = h / NUM_SLICE - 1;
                imgb.SizeMode = PictureBoxSizeMode.StretchImage;
                imgb.Visible = true;
                
                imgbox[i] = imgb;
                panel_imgs.Controls.Add(imgbox[i]);
            }
            //Thread t_getImg = new Thread(ProcessFram);
            //t_getImg.Start();

            // 在下拉文本框cbb_localIP中显示该计算机中的IPv4地址
            cbb_localIP.Text = GetLocalIPv4Address().ToString();
        }
        private VideoCapture capture = new VideoCapture();

        const int WIDTH = 1920;  // 设置读取的图片宽度
        const int HEIGHT = 1080; // 设置读取的图片高度
        const int NUM_SLICE = 24; // 切片数量
        ImageBox[] imgbox = new ImageBox[NUM_SLICE];
        Mat[] img = new Mat[NUM_SLICE];       
        private void ProcessFram() // 图像读取、切片、发送
        {
            DateTime startDT = System.DateTime.Now;
            while (true)
            {
                // 计算两次循环间的间隔，并显示在左上角
                DateTime stopDT = System.DateTime.Now;
                TimeSpan ts = stopDT.Subtract(startDT);
                this.Text = "图片处理耗时：" + ts.TotalMilliseconds + "ms";
                startDT = System.DateTime.Now;

                // 读取一张图片
                Mat currentImage = capture.QueryFrame();
                // 显示摄像头/视频的图像
                imageBox0.Image = currentImage;

                int N = HEIGHT / NUM_SLICE;
                
                // 将切片压缩后发送到接收端                
                Parallel.For(0, NUM_SLICE, i => // Parallel并进加速
                //for(int i = 0; i < NUM_SLICE; i++) // 普通串行方法
                    {
                        // 从原图中切割，输入参数：原始图片 行范围row 列范围col
                        img[i] = new Mat(currentImage, new Range(i * N, (i + 1) * N - 1), new Range(0, WIDTH));
                        // 显示
                        imgbox[i].Image = img[i];

                        // 转换格式
                        Image<Rgb, Byte> img_trans = img[i].ToImage<Rgb, Byte>();
                        // JPEG压缩
                        byte[] bytes = img_trans.ToJpegData(95);
                       

                        // UDP配置
                        UdpClient udpClient = new UdpClient();
                        //IPAddress ipaddress = IPAddress.Parse("192.168.0.105");
                        IPAddress ipaddress = remoteIP;
                        IPEndPoint endpoint = new IPEndPoint(ipaddress, 8000 + 10 * i);

                        // 添加时间戳
                        //byte[] sec = { (byte)((((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000) & 0xFF00) >> 8),
                        //            (byte)(((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000) & 0xFF)};
                        //bytes.Concat(sec);

                        // UDP发送
                        udpClient.Send(bytes, bytes.Length, endpoint);
                        udpClient.Close();
                    }
                );
                
                
            }
        }
        //-------------- TCP连接 ---------------
        // 创建套接字
        Thread threadWatch = null; //负责监听客户端的线程
        Socket socketWatch = null;  //负责监听客户端的套接字  
        private void openBtn_Click(object sender, EventArgs e)
        {
            try
            {
                //定义一个套接字用于监听客户端发来的信息  包含3个参数(IP4寻址协议,流式连接,TCP协议)
                socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //服务端发送信息 需要1个IP地址和端口号
                IPAddress ipaddress = IPAddress.Parse(this.cbb_localIP.Text.Trim()); //获取文本框输入的IP地址
                                                                                     //将IP地址和端口号绑定到网络节点endpoint上 
                IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.cbb_localPort.Text.Trim())); //获取文本框上输入的端口号
                                                                                                            //监听绑定的网络节点
                socketWatch.Bind(endpoint);
                //将套接字的监听队列长度限制为20
                socketWatch.Listen(20);
                //创建一个监听线程 
                threadWatch = new Thread(WatchConnecting);
                //将窗体线程设置为与后台同步
                threadWatch.IsBackground = true;
                //启动线程
                threadWatch.Start();

                openBtn.Enabled = false;
                this.Text = "开始监听......";
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }
        // 监听客户端发来的请求
        IPAddress remoteIP = null;
        private void WatchConnecting()
        {
            while (true)  //持续不断监听客户端发来的请求
            {

                Socket soc = socketWatch.Accept();
                remoteIP = ((IPEndPoint)soc.RemoteEndPoint).Address;
                soc.Close();
                this.Text = "连接成功！";       
                // 休眠1s
                Thread.Sleep(1000);
                this.Text = "远端IP:" + remoteIP.ToString();

                // 连接成功后开始发送图片
                Thread transFrames = new Thread(ProcessFram);
                transFrames.Start();
            }
        }

        public IPAddress GetLocalIPv4Address()
        {
            IPAddress localIpv4 = null;
            //获取本机所有的IP地址列表
            IPAddress[] IpList = Dns.GetHostAddresses(Dns.GetHostName());
            //循环遍历所有IP地址
            foreach (IPAddress IP in IpList)
            {
                //判断是否是IPv4地址
                if (IP.AddressFamily == AddressFamily.InterNetwork)
                {
                    cbb_localIP.Items.Add(IP);
                    localIpv4 = IP;
                    //break;
                }
            }
            return localIpv4;
        }
    }
}
